package cn.lizemin.service.impl;

import cn.lizemin.dao.OrderDAO;
import cn.lizemin.dao.StockDAO;
import cn.lizemin.dao.UserDAO;
import cn.lizemin.entity.Order;
import cn.lizemin.entity.Stock;
import cn.lizemin.entity.User;
import cn.lizemin.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author lzm
 * @version 1.0
 * @description: 秒杀服务
 * @date 2022/3/20 15:12
 */
@Service
@Slf4j
@Transactional
public class OrderServiceImpl implements OrderService {

    @Autowired
    private StockDAO stockDAO;

    @Autowired
    private OrderDAO orderDAO;

    @Autowired
    private UserDAO userDAO;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 开发秒杀方法
     * 未考虑线程安全的接口，结果出现了超卖的情况
     * @param id
     * @return
     */
    @Override
    public int kill(Integer id) {

        //校验redis中秒杀商品是否超时, 这里主要是模拟秒杀的限时抢购
        if (!stringRedisTemplate.hasKey("kill")) {
            throw new RuntimeException("当前商品的抢购活动已经结束啦~~");
        }

        //根据商品id校验库存
        Stock stock = checkStock(id);

        //更新库存, 对方买了一个
        updateSale(stock);

        //创建订单
        Integer oid = createOrder(stock);

//        返回主键自增的订单id
        return oid;
    }

    /**
     * 校验库存
     */
    private Stock checkStock(Integer id) {
        Stock stock = stockDAO.checkStock(id);
        if (stock.getSale().equals(stock.getCount())) {
            throw new RuntimeException("库存不足！");
        }
        return stock;
    }

    /**
     * 更新库存
     */
    //扣除库存
    private void updateSale(Stock stock){
        //在sql层面完成销量的+1  和 版本号的+  并且根据商品id和版本号同时查询更新的商品
        int updateRows = stockDAO.updateSale(stock);
        if (updateRows==0){
            throw new RuntimeException("抢购失败,请重试!!!");
        }
    }

    /**
     * 创建订单
     * @param stock
     * @return
     */
    private Integer createOrder(Stock stock) {
        Order order = new Order();
        order.setSid(stock.getId()).setName(stock.getName()).setCreateDate(new Date());
        orderDAO.createOrder(order);
        return order.getId();
    }




    /**
     * 使用同步方法解决超卖问题，可以解决超卖问题，但是锁的粒度是方法级别的，太大了
     * @param id
     * @return
     */
//    @Override//普通同步方法，锁的是this
//    public synchronized int kill(Integer id) {
//        //根据商品id校验库存
//        Stock stock = stockDAO.checkStock(id);
//        if (stock.getSale().equals(stock.getCount())) {
//            throw new RuntimeException("库存不足！");
//        }
//
//        //更新库存, 对方买了一个
//        stock.setSale(stock.getSale() + 1);
//        stockDAO.updateSale(stock);
//
//        //创建订单
//        Order order = new Order();
//        order.setSid(stock.getId()).setName(stock.getName()).setCreateDate(new Date());
//        orderDAO.createOrder(order);
////        返回主键自增的订单id
//        return order.getId();
//    }

    @Override
    public int kill(Integer id, Integer userid, String md5) {

        //校验redis中秒杀商品是否超时
//        if(!stringRedisTemplate.hasKey("kill"+id))
//            throw new RuntimeException("当前商品的抢购活动已经结束啦~~");

        //先验证签名
        String hashKey = "KEY_"+userid+"_"+id;
        String s = stringRedisTemplate.opsForValue().get(hashKey);
        if (s==null) throw  new RuntimeException("没有携带验证签名,请求不合法!");
        if (!s.equals(md5)) throw  new RuntimeException("当前请求数据不合法,请稍后再试!");

        //校验库存
        Stock stock = checkStock(id);
        //更新库存
        updateSale(stock);
        //创建订单
        return createOrder(stock);
    }

    @Override
    public String getMd5(Integer id, Integer userid) {

        //检验用户的合法性
        User user = userDAO.findById(userid);
        if(user==null)throw new RuntimeException("用户信息不存在!");
        log.info("用户信息:[{}]",user.toString());

        //检验商品的合法行
        Stock stock = stockDAO.checkStock(id);
        if(stock==null) throw new RuntimeException("商品信息不合法!");
        log.info("商品信息:[{}]",stock.toString());

        //生成hashkey
        String hashKey = "KEY_"+userid+"_"+id;
        //生成md5 ,这里!QS#是一个盐 随机生成
        String token = DigestUtils.md5DigestAsHex((userid+id+"!Q*jS#").getBytes());
        stringRedisTemplate.opsForValue().set(hashKey, token, 120, TimeUnit.SECONDS);

        log.info("Redis写入：[{}] [{}]", hashKey, token);
        return token;
    }





}


